// 版权归Go作者所有。版权所有。
// 此源代码的使用受BSD样式的约束
// 可以在许可证文件中找到的许可证。

package modload

import (
	"bytes"
	"context"
	"errors"
	"fmt"
	"io/fs"
	"os"
	pathpkg "path"
	"sort"
	"strings"
	"sync"
	"time"

	"cmd/go/internal/cfg"
	"cmd/go/internal/imports"
	"cmd/go/internal/modfetch"
	"cmd/go/internal/search"
	"cmd/go/internal/str"
	"cmd/go/internal/trace"

	"golang.org/x/mod/module"
	"golang.org/x/mod/semver"
)

// 查询在给定版本查询字符串的情况下查找给定模块的修订版。
// 模块必须是完整的模块路径。
// 版本必须采用以下形式之一：
// None
// -文字字符串“latest”，表示允许的最新可用值
// 标记版本，非预发布版本优先于预发布版本。
// 如果回购协议中没有标记的版本，则“最新”返回的版本最多
// 最近的承诺。
// -文字字符串“upgrade”，相当于“latest”，除非
// current是较新的版本，将返回current（见下文）。
// -文本字符串“patch”，表示最新的可用标记版本
// 具有与当前相同的主要和次要编号（见下文）。
// -v1，表示最新的可用标记版本v1.x.x。
// -v1.2，表示最新的可用标记版本v1.2.x。
// -v1.2.3，表示标记版本的语义版本字符串。
// -<v1.2.3，<=v1.2.3，>v1.2.3，>=v1.2.3，
// 表示最接近目标并满足给定运算符的版本，
// 非预发布优先于预发布。
// -存储库提交标识符或标记，表示该提交。
// None
// current表示当前选择的模块版本；可能是
// 如果当前未选择任何版本，则为“无”；如果当前已选择版本，则为“无”
// 版本未知或不应考虑。如果查询是
// “升级”或“补丁”，如果是较新版本，将返回当前版本
// 语义版本或按时间顺序晚于
// 否则将选择的版本。这可以防止意外降级
// 从较新的预发布或开发版本。
// None
// 允许的函数（可能为零）用于筛选不合适的函数
// 版本（有关详细信息，请参阅AllowedFunc文档）。如果查询引用
// 特定修订版（例如，“master”；请参阅IsRevisionQuery），以及
// “允许”不允许修订，查询返回错误。如果查询
// 不引用特定修订（例如，“最新”），查询
// 就好像“允许”不允许的版本不存在一样。
// None
// 如果路径是主模块的路径，查询为“最新”，
// 查询返回Target.Version作为版本。
func Query(ctx context.Context, path, query, current string, allowed AllowedFunc) (*modfetch.RevInfo, error) {
	var info *modfetch.RevInfo
	err := modfetch.TryProxies(func(proxy string) (err error) {
		info, err = queryProxy(ctx, proxy, path, query, current, allowed)
		return err
	})
	return info, err
}

// AllowedFunc由查询和其他函数用于筛选不合适的函数
// 版本，例如，主目录中排除指令中列出的版本
// 模块的go.mod文件。
// None
// AllowedFunc返回一个错误，该错误相当于一个不合适的函数的ErrDisallowed
// 版本任何其他错误都表明函数无法确定
// 是否应允许该版本，例如，函数无法
// 获取或分析包含收回的go.mod文件。通常，错误
// 除了ErrDisallowd之外，其他内容可能会被忽略。
type AllowedFunc func(context.Context, module.Version) error

var errQueryDisabled error = queryDisabledError{}

type queryDisabledError struct{}

func (queryDisabledError) Error() string {
	if cfg.BuildModReason == "" {
		return fmt.Sprintf("cannot query module due to -mod=%s", cfg.BuildMod)
	}
	return fmt.Sprintf("cannot query module due to -mod=%s\n\t(%s)", cfg.BuildMod, cfg.BuildModReason)
}

func queryProxy(ctx context.Context, proxy, path, query, current string, allowed AllowedFunc) (*modfetch.RevInfo, error) {
	ctx, span := trace.StartSpan(ctx, "modload.queryProxy "+path+" "+query)
	defer span.Done()

	if current != "" && current != "none" && !semver.IsValid(current) {
		return nil, fmt.Errorf("invalid previous version %q", current)
	}
	if cfg.BuildMod == "vendor" {
		return nil, errQueryDisabled
	}
	if allowed == nil {
		allowed = func(context.Context, module.Version) error { return nil }
	}

	if path == Target.Path && (query == "upgrade" || query == "patch") {
		if err := allowed(ctx, Target); err != nil {
			return nil, fmt.Errorf("internal error: main module version is not allowed: %w", err)
		}
		return &modfetch.RevInfo{Version: Target.Version}, nil
	}

	if path == "std" || path == "cmd" {
		return nil, fmt.Errorf("can't query specific version (%q) of standard-library module %q", query, path)
	}

	repo, err := lookupRepo(proxy, path)
	if err != nil {
		return nil, err
	}

	// 解析查询以检测解析错误（并可能处理查询）
	// 在任何网络I/O之前。
	qm, err := newQueryMatcher(path, query, current, allowed)
	if (err == nil && qm.canStat) || err == errRevQuery {
		// 直接查找提交标识符或完整（非前缀）语义
		// 版本

		// 如果标识符不是规范的semver标记-包括如果它是
		// 带有+元数据后缀的semver标记-然后将填充modfetch.Stat
		// 具有合适的伪版本的info.Version。
		info, err := repo.Stat(query)
		if err != nil {
			queryErr := err
			// 完整查询与标记不对应。如果是语义版本
			// 使用+元数据后缀，查看是否有不带该后缀的标记：
			// 语义版本控制将它们定义为等价的。
			canonicalQuery := module.CanonicalVersion(query)
			if canonicalQuery != "" && query != canonicalQuery {
				info, err = repo.Stat(canonicalQuery)
				if err != nil && !errors.Is(err, fs.ErrNotExist) {
					return info, err
				}
			}
			if err != nil {
				return nil, queryErr
			}
		}
		if err := allowed(ctx, module.Version{Path: path, Version: info.Version}); errors.Is(err, ErrDisallowed) {
			return nil, err
		}
		return info, nil
	} else if err != nil {
		return nil, err
	}

	// 加载版本并执行查询。
	versions, err := repo.Versions(qm.prefix)
	if err != nil {
		return nil, err
	}
	releases, prereleases, err := qm.filterVersions(ctx, versions)
	if err != nil {
		return nil, err
	}

	lookup := func(v string) (*modfetch.RevInfo, error) {
		rev, err := repo.Stat(v)
		if err != nil {
			return nil, err
		}

		if (query == "upgrade" || query == "patch") && module.IsPseudoVersion(current) && !rev.Time.IsZero() {
			// 不允许“升级”或“补丁”从伪版本移动
			// 到按时间顺序排列的旧版本或伪版本。
			// None
			// 如果当前版本是来自未标记分支的伪版本，则
			// 可能在语义上低于“最新”版本或最新版本
			// 主分支上的伪版本。用户不太可能使用这种版本
			// 打算“升级”到当时已经存在的版本
			// 及时
			// None
			// 我们只在当前版本是伪版本时才这样做：如果
			// 版本被标记，依赖模块的作者给了我们
			// 关于此版本的预期优先级的明确信息
			// 相对于其他版本，我们不应该反驳这一点
			// 信息（例如，v1.0.1可能已经是修复程序的后端口
			// 合并到v1.1.0中，在这种情况下，v1.0.1将按时间顺序排列
			// 更新，但v1.1.0仍然是一个“升级”；或者v1.0.2可能是
			// v1.0.1中的不成功修复，在这种情况下，v1.0.2提交可能会失败
			// 尽管标记本身较新，但仍比v1.0.1提交版本旧。）
			currentTime, err := module.PseudoVersionTime(current)
			if err == nil && rev.Time.Before(currentTime) {
				if err := allowed(ctx, module.Version{Path: path, Version: current}); errors.Is(err, ErrDisallowed) {
					return nil, err
				}
				return repo.Stat(current)
			}
		}

		return rev, nil
	}

	if qm.preferLower {
		if len(releases) > 0 {
			return lookup(releases[0])
		}
		if len(prereleases) > 0 {
			return lookup(prereleases[0])
		}
	} else {
		if len(releases) > 0 {
			return lookup(releases[len(releases)-1])
		}
		if len(prereleases) > 0 {
			return lookup(prereleases[len(prereleases)-1])
		}
	}

	if qm.mayUseLatest {
		latest, err := repo.Latest()
		if err == nil {
			if qm.allowsVersion(ctx, latest.Version) {
				return lookup(latest.Version)
			}
		} else if !errors.Is(err, fs.ErrNotExist) {
			return nil, err
		}
	}

	if (query == "upgrade" || query == "patch") && current != "" && current != "none" {
		// 如果允许，“升级”和“补丁”可能保留在当前版本上。
		if err := allowed(ctx, module.Version{Path: path, Version: current}); errors.Is(err, ErrDisallowed) {
			return nil, err
		}
		return lookup(current)
	}

	return nil, &NoMatchingVersionError{query: query, current: current}
}

// 如果vers是可能引用的版本查询，IsRevisionQuery将返回true
// 存储库中的特定版本或修订版，如“v1.0.0”、“master”，
// 或“0123abcd”。如果vers是一个
// 从“最新”或“>v1.0.0”等可用版本中进行选择。
func IsRevisionQuery(vers string) bool {
	if vers == "latest" ||
		vers == "upgrade" ||
		vers == "patch" ||
		strings.HasPrefix(vers, "<") ||
		strings.HasPrefix(vers, ">") ||
		(semver.IsValid(vers) && isSemverPrefix(vers)) {
		return false
	}
	return true
}

// isSemverPrefix报告v是语义版本前缀：v1还是v1.2（不是v1.2.3）。
// 假定调用方已检查semver.IsValid（v）是否为true。
func isSemverPrefix(v string) bool {
	dots := 0
	for i := 0; i < len(v); i++ {
		switch v[i] {
		case '-', '+':
			return false
		case '.':
			dots++
			if dots >= 2 {
				return false
			}
		}
	}
	return true
}

type queryMatcher struct {
	path               string
	prefix             string
	filter             func(version string) bool
	allowed            AllowedFunc
	canStat            bool // 如果为true，则可以通过repo.Stat解析查询
	preferLower        bool // 如果为true，请选择最低匹配版本
	mayUseLatest       bool
	preferIncompatible bool
}

var errRevQuery = errors.New("query refers to a non-semver revision")

// newQueryMatcher返回与版本匹配的新queryMatcher
// 由具有给定路径的模块上的给定查询指定。
// None
// 如果查询只能通过标记非SemVer修订来解决，
// newQueryMatcher返回errRevQuery。
func newQueryMatcher(path string, query, current string, allowed AllowedFunc) (*queryMatcher, error) {
	badVersion := func(v string) (*queryMatcher, error) {
		return nil, fmt.Errorf("invalid semantic version %q in range %q", v, query)
	}

	matchesMajor := func(v string) bool {
		_, pathMajor, ok := module.SplitPathVersion(path)
		if !ok {
			return false
		}
		return module.CheckPathMajor(v, pathMajor) == nil
	}

	qm := &queryMatcher{
		path:               path,
		allowed:            allowed,
		preferIncompatible: strings.HasSuffix(current, "+incompatible"),
	}

	switch {
	case query == "latest":
		qm.mayUseLatest = true

	case query == "upgrade":
		if current == "" || current == "none" {
			qm.mayUseLatest = true
		} else {
			qm.mayUseLatest = module.IsPseudoVersion(current)
			qm.filter = func(mv string) bool { return semver.Compare(mv, current) >= 0 }
		}

	case query == "patch":
		if current == "" || current == "none" {
			return nil, &NoPatchBaseError{path}
		}
		if current == "" {
			qm.mayUseLatest = true
		} else {
			qm.mayUseLatest = module.IsPseudoVersion(current)
			qm.prefix = semver.MajorMinor(current) + "."
			qm.filter = func(mv string) bool { return semver.Compare(mv, current) >= 0 }
		}

	case strings.HasPrefix(query, "<="):
		v := query[len("<="):]
		if !semver.IsValid(v) {
			return badVersion(v)
		}
		if isSemverPrefix(v) {
			// 拒绝说<=v1.2是否允许v1.2.3（记住，@v1.2可能意味着v1.2.3）。
			return nil, fmt.Errorf("ambiguous semantic version %q in range %q", v, query)
		}
		qm.filter = func(mv string) bool { return semver.Compare(mv, v) <= 0 }
		if !matchesMajor(v) {
			qm.preferIncompatible = true
		}

	case strings.HasPrefix(query, "<"):
		v := query[len("<"):]
		if !semver.IsValid(v) {
			return badVersion(v)
		}
		qm.filter = func(mv string) bool { return semver.Compare(mv, v) < 0 }
		if !matchesMajor(v) {
			qm.preferIncompatible = true
		}

	case strings.HasPrefix(query, ">="):
		v := query[len(">="):]
		if !semver.IsValid(v) {
			return badVersion(v)
		}
		qm.filter = func(mv string) bool { return semver.Compare(mv, v) >= 0 }
		qm.preferLower = true
		if !matchesMajor(v) {
			qm.preferIncompatible = true
		}

	case strings.HasPrefix(query, ">"):
		v := query[len(">"):]
		if !semver.IsValid(v) {
			return badVersion(v)
		}
		if isSemverPrefix(v) {
			// 拒绝说明>v1.2是否允许v1.2.3（记住，@v1.2可能意味着v1.2.3）。
			return nil, fmt.Errorf("ambiguous semantic version %q in range %q", v, query)
		}
		qm.filter = func(mv string) bool { return semver.Compare(mv, v) > 0 }
		qm.preferLower = true
		if !matchesMajor(v) {
			qm.preferIncompatible = true
		}

	case semver.IsValid(query):
		if isSemverPrefix(query) {
			qm.prefix = query + "."
			// 不允许查询“v1.2”匹配低于“v1.2.0”的版本，
			// 例如该版本的预发布。（https:
			qm.filter = func(mv string) bool { return semver.Compare(mv, query) >= 0 }
		} else {
			qm.canStat = true
			qm.filter = func(mv string) bool { return semver.Compare(mv, query) == 0 }
			qm.prefix = semver.Canonical(query)
		}
		if !matchesMajor(query) {
			qm.preferIncompatible = true
		}

	default:
		return nil, errRevQuery
	}

	return qm, nil
}

// allowsVersion报告前缀、筛选器和
// qm的AllowedFunc。
func (qm *queryMatcher) allowsVersion(ctx context.Context, v string) bool {
	if qm.prefix != "" && !strings.HasPrefix(v, qm.prefix) {
		return false
	}
	if qm.filter != nil && !qm.filter(v) {
		return false
	}
	if qm.allowed != nil {
		if err := qm.allowed(ctx, module.Version{Path: qm.path, Version: v}); errors.Is(err, ErrDisallowed) {
			return false
		}
	}
	return true
}

// filterVersions将版本分为发行版和预发行版，并进行筛选
// 输出：
// 1.不满足“允许”谓词的版本，以及
// 2.“+不兼容”版本，如果兼容版本满足谓词
// 不兼容的版本不是首选。
// None
// 如果allowed谓词返回的错误不等于ErrDisallowed，
// filterVersions返回该错误。
func (qm *queryMatcher) filterVersions(ctx context.Context, versions []string) (releases, prereleases []string, err error) {
	needIncompatible := qm.preferIncompatible

	var lastCompatible string
	for _, v := range versions {
		if !qm.allowsVersion(ctx, v) {
			continue
		}

		if !needIncompatible {
			// 我们还不确定是否需要包含+不可数版本。
			// 跟踪我们看到的最后一个兼容版本，并使用
			// 在该版本中是否存在go.mod文件以决定：
			// go.mod文件意味着模块作者在某个级别支持模块
			// 兼容版本（我们应该忽略+不兼容版本，除非
			// 明确请求），而缺少go.mod文件意味着
			// 无语义导入的遗留（预模块）版本控制的可能性
			// 路径（因此带有*+不兼容版本）。
			// None
			// 如果已安装最新的兼容版本，则这并不严格准确
			// 替换为本地文件路径，因为我们不允许文件路径
			// 没有go.mod文件的替换：用户需要添加
			// 一但是，更换上一个兼容版本时
			// 同时希望隐式升级到+不兼容
			// 这个版本似乎是一个极端的情况，现在可以忽略不计。

			if !strings.HasSuffix(v, "+incompatible") {
				lastCompatible = v
			} else if lastCompatible != "" {
				// 如果允许使用最新的兼容版本并具有go.mod文件，
				// 忽略主版本较高（+不兼容）的任何版本。（见
				// https:
				// 兼容的预发布版本优于不兼容的发布版本。
				ok, err := versionHasGoMod(ctx, module.Version{Path: qm.path, Version: lastCompatible})
				if err != nil {
					return nil, nil, err
				}
				if ok {
					// 最后一个兼容版本有一个go.mod文件，因此
					// 我们愿意考虑的最高版本。甚至不用麻烦了
					// 看看更高的版本，因为它们都是+不兼容的
					// 从这里开始。
					break
				}

				// 没有可接受的兼容版本具有go.mod文件，因此版本控制
				// 因为模块可能没有模块意识，我们应该尊重
				// 遗留主要版本标签。
				needIncompatible = true
			}
		}

		if semver.Prerelease(v) != "" {
			prereleases = append(prereleases, v)
		} else {
			releases = append(releases, v)
		}
	}

	return releases, prereleases, nil
}

type QueryResult struct {
	Mod      module.Version
	Rev      *modfetch.RevInfo
	Packages []string
}

// QueryPackages与QueryPattern类似，但要求模式在
// 至少一个包，并忽略非包结果（如果有）。
func QueryPackages(ctx context.Context, pattern, query string, current func(string) string, allowed AllowedFunc) ([]QueryResult, error) {
	pkgMods, modOnly, err := QueryPattern(ctx, pattern, query, current, allowed)

	if len(pkgMods) == 0 && err == nil {
		return nil, &PackageNotInModuleError{
			Mod:         modOnly.Mod,
			Replacement: Replacement(modOnly.Mod),
			Query:       query,
			Pattern:     pattern,
		}
	}

	return pkgMods, err
}

// QueryPattern查找至少包含一个包匹配的模块
// 给定版本的给定模式。结果按模块路径排序
// 长度按降序排列。如果任何代理提供一个非空的候选集
// 模块，不再尝试其他代理。
// None
// 对于通配符模式，QueryPattern在包路径最多为的模块中查找
// 模式中的第一个“…”。对于模式“example.com/a/b../c”，
// QueRead模式将考虑“Simult.COM/A”的前缀。
// None
// 如果主模块中有任何匹配的包，QueryPattern只考虑
// 主模块仅为“最新”版本，不检查其他版本
// 可能的模块。
// None
// QueryPattern始终返回至少一个QueryResult（可能是
// modOnly）或非零错误。
func QueryPattern(ctx context.Context, pattern, query string, current func(string) string, allowed AllowedFunc) (pkgMods []QueryResult, modOnly *QueryResult, err error) {
	ctx, span := trace.StartSpan(ctx, "modload.QueryPattern "+pattern+" "+query)
	defer span.Done()

	base := pattern

	firstError := func(m *search.Match) error {
		if len(m.Errs) == 0 {
			return nil
		}
		return m.Errs[0]
	}

	var match func(mod module.Version, root string, isLocal bool) *search.Match
	matchPattern := search.MatchPattern(pattern)

	if i := strings.Index(pattern, "..."); i >= 0 {
		base = pathpkg.Dir(pattern[:i+3])
		if base == "." {
			return nil, nil, &WildcardInFirstElementError{Pattern: pattern, Query: query}
		}
		match = func(mod module.Version, root string, isLocal bool) *search.Match {
			m := search.NewMatch(pattern)
			matchPackages(ctx, m, imports.AnyTags(), omitStd, []module.Version{mod})
			return m
		}
	} else {
		match = func(mod module.Version, root string, isLocal bool) *search.Match {
			m := search.NewMatch(pattern)
			prefix := mod.Path
			if mod == Target {
				prefix = targetPrefix
			}
			if _, ok, err := dirInModule(pattern, prefix, root, isLocal); err != nil {
				m.AddError(err)
			} else if ok {
				m.Pkgs = []string{pattern}
			}
			return m
		}
	}

	var queryMatchesMainModule bool
	if HasModRoot() {
		m := match(Target, modRoot, true)
		if len(m.Pkgs) > 0 {
			if query != "upgrade" && query != "patch" {
				return nil, nil, &QueryMatchesPackagesInMainModuleError{
					Pattern:  pattern,
					Query:    query,
					Packages: m.Pkgs,
				}
			}
			if err := allowed(ctx, Target); err != nil {
				return nil, nil, fmt.Errorf("internal error: package %s is in the main module (%s), but version is not allowed: %w", pattern, Target.Path, err)
			}
			return []QueryResult{{
				Mod:      Target,
				Rev:      &modfetch.RevInfo{Version: Target.Version},
				Packages: m.Pkgs,
			}}, nil, nil
		}
		if err := firstError(m); err != nil {
			return nil, nil, err
		}

		if matchPattern(Target.Path) {
			queryMatchesMainModule = true
		}

		if (query == "upgrade" || query == "patch") && queryMatchesMainModule {
			if err := allowed(ctx, Target); err == nil {
				modOnly = &QueryResult{
					Mod: Target,
					Rev: &modfetch.RevInfo{Version: Target.Version},
				}
			}
		}
	}

	var (
		results          []QueryResult
		candidateModules = modulePrefixesExcludingTarget(base)
	)
	if len(candidateModules) == 0 {
		if modOnly != nil {
			return nil, modOnly, nil
		} else if queryMatchesMainModule {
			return nil, nil, &QueryMatchesMainModuleError{
				Pattern: pattern,
				Query:   query,
			}
		} else {
			return nil, nil, &PackageNotInModuleError{
				Mod:     Target,
				Query:   query,
				Pattern: pattern,
			}
		}
	}

	err = modfetch.TryProxies(func(proxy string) error {
		queryModule := func(ctx context.Context, path string) (r QueryResult, err error) {
			ctx, span := trace.StartSpan(ctx, "modload.QueryPattern.queryModule ["+proxy+"] "+path)
			defer span.Done()

			pathCurrent := current(path)
			r.Mod.Path = path
			r.Rev, err = queryProxy(ctx, proxy, path, query, pathCurrent, allowed)
			if err != nil {
				return r, err
			}
			r.Mod.Version = r.Rev.Version
			needSum := true
			root, isLocal, err := fetch(ctx, r.Mod, needSum)
			if err != nil {
				return r, err
			}
			m := match(r.Mod, root, isLocal)
			r.Packages = m.Pkgs
			if len(r.Packages) == 0 && !matchPattern(path) {
				if err := firstError(m); err != nil {
					return r, err
				}
				return r, &PackageNotInModuleError{
					Mod:         r.Mod,
					Replacement: Replacement(r.Mod),
					Query:       query,
					Pattern:     pattern,
				}
			}
			return r, nil
		}

		allResults, err := queryPrefixModules(ctx, candidateModules, queryModule)
		results = allResults[:0]
		for _, r := range allResults {
			if len(r.Packages) == 0 {
				modOnly = &r
			} else {
				results = append(results, r)
			}
		}
		return err
	})

	if queryMatchesMainModule && len(results) == 0 && modOnly == nil && errors.Is(err, fs.ErrNotExist) {
		return nil, nil, &QueryMatchesMainModuleError{
			Pattern: pattern,
			Query:   query,
		}
	}
	return results[:len(results):len(results)], modOnly, err
}

// modulePrefixesExcludingTarget返回路径的所有前缀，这些前缀可能
// 作为模块存在，不包括targetPrefix，但包括路径
// 本身，按降序长度排序。不是有效模块路径的前缀
// 但是否包含有效的包路径（如“m”或“example.com/.gen”），
// 因为它们可能会被替换。
func modulePrefixesExcludingTarget(path string) []string {
	prefixes := make([]string, 0, strings.Count(path, "/")+1)

	for {
		if path != targetPrefix {
			if _, _, ok := module.SplitPathVersion(path); ok {
				prefixes = append(prefixes, path)
			}
		}

		j := strings.LastIndexByte(path, '/')
		if j < 0 {
			break
		}
		path = path[:j]
	}

	return prefixes
}

func queryPrefixModules(ctx context.Context, candidateModules []string, queryModule func(ctx context.Context, path string) (QueryResult, error)) (found []QueryResult, err error) {
	ctx, span := trace.StartSpan(ctx, "modload.queryPrefixModules")
	defer span.Done()

	// 如果我们正在尝试的路径不在模块缓存中，并且我们没有
	// 获取缓存的结果，我们最终将生成一个（可能很慢）
	// 请求到代理服务器或（通常更慢）源服务器。
	// 要最小化延迟，请并行执行所有这些请求。
	type result struct {
		QueryResult
		err error
	}
	results := make([]result, len(candidateModules))
	var wg sync.WaitGroup
	wg.Add(len(candidateModules))
	for i, p := range candidateModules {
		ctx := trace.StartGoroutine(ctx)
		go func(p string, r *result) {
			r.QueryResult, r.err = queryModule(ctx, p)
			wg.Done()
		}(p, &results[i])
	}
	wg.Wait()

	// 对结果进行分类。如果出现故障，请确定用户可能遇到的错误
	// 最有可能发现有用的错误：在
	// 最长匹配路径。
	var (
		noPackage   *PackageNotInModuleError
		noVersion   *NoMatchingVersionError
		noPatchBase *NoPatchBaseError
		invalidPath *module.InvalidPathError // 请参见下面案例中的注释
		notExistErr error
	)
	for _, r := range results {
		switch rErr := r.err.(type) {
		case nil:
			found = append(found, r.QueryResult)
		case *PackageNotInModuleError:
			// 如果提供该选项，则首选属性“包不在模块中”
			// 到主模块以外的模块。
			if noPackage == nil || noPackage.Mod == Target {
				noPackage = rErr
			}
		case *NoMatchingVersionError:
			if noVersion == nil {
				noVersion = rErr
			}
		case *NoPatchBaseError:
			if noPatchBase == nil {
				noPatchBase = rErr
			}
		case *module.InvalidPathError:
			// 前缀不是有效的模块路径，没有替换。
			// 这样的前缀可能出现在candidateModules中，因为我们处理
			// 替换了回购查找过程中不需要的模块
			// （见lookupRepo）。
			// None
			// 较短的前缀可能是有效的模块路径，并且可能包含有效的
			// 导入路径，因此这是一个低优先级错误。
			if invalidPath == nil {
				invalidPath = rErr
			}
		default:
			if errors.Is(rErr, fs.ErrNotExist) {
				if notExistErr == nil {
					notExistErr = rErr
				}
			} else if err == nil {
				if len(found) > 0 || noPackage != nil {
					// golang.org/issue/34094：如果我们已经找到一个模块
					// 可能包含目标包，忽略未分类
					// 路径较短的模块出现错误。

					// golang.org/issue/34383是一个特例：如果我们有
					// 已经找到example.com/foo/v2@v2.0.0使用匹配的go.mod
					// 文件，忽略example.com中的错误/foo@v2.0.0.
				} else {
					err = r.err
				}
			}
		}
	}

	// TODO（#26232）：如果len（found）=0，并且一些错误是4xx HTTP
	// 代码，让身份验证包重新检查失败的路径。
	// 如果我们为其中任何一个获取了新凭据，请重新运行上述循环。

	if len(found) == 0 && err == nil {
		switch {
		case noPackage != nil:
			err = noPackage
		case noVersion != nil:
			err = noVersion
		case noPatchBase != nil:
			err = noPatchBase
		case invalidPath != nil:
			err = invalidPath
		case notExistErr != nil:
			err = notExistErr
		default:
			panic("queryPrefixModules: no modules found, but no error detected")
		}
	}

	return found, err
}

// NoMatchingVersionError表示查询在请求的位置找到模块
// 路径，但不在满足查询字符串和allow函数的任何版本。
// None
// 注意：NoMatchingVersionError不能实现Is（fs.ErrNotExist）。
// None
// 如果模块来自代理，则该代理必须返回成功状态
// 它知道的版本的代码，因此没有机会
// 返回非400状态代码以抑制回退。
type NoMatchingVersionError struct {
	query, current string
}

func (e *NoMatchingVersionError) Error() string {
	currentSuffix := ""
	if (e.query == "upgrade" || e.query == "patch") && e.current != "" && e.current != "none" {
		currentSuffix = fmt.Sprintf(" (current version is %s)", e.current)
	}
	return fmt.Sprintf("no matching versions for query %q", e.query) + currentSuffix
}

// NoPatchBaseError表示使用查询“补丁”调用了查询
// 但使用当前版本的“”或“无”。
type NoPatchBaseError struct {
	path string
}

func (e *NoPatchBaseError) Error() string {
	return fmt.Sprintf(`can't query version "patch" of module %s: no existing version is required`, e.path)
}

// 通配符InfirsTelementError表示传递给QueryPattern的模式
// 在其第一个path元素中有一个通配符，因此没有模式前缀
// 要搜索的模块。
type WildcardInFirstElementError struct {
	Pattern string
	Query   string
}

func (e *WildcardInFirstElementError) Error() string {
	return fmt.Sprintf("no modules to query for %s@%s because first path element contains a wildcard", e.Pattern, e.Query)
}

// PackageNotModuleError表示QueryPattern找到了一个候选者
// 模块，但该模块不包含任何包
// 匹配请求的模式。
// None
// 注意：PackageNotModuleError不能实现Is（fs.ErrNotExist）。
// None
// 如果模块来自代理，则该代理必须返回成功状态
// 它知道的版本的代码，因此没有机会
// 返回非400状态代码以抑制回退。
type PackageNotInModuleError struct {
	Mod         module.Version
	Replacement module.Version
	Query       string
	Pattern     string
}

func (e *PackageNotInModuleError) Error() string {
	if e.Mod == Target {
		if strings.Contains(e.Pattern, "...") {
			return fmt.Sprintf("main module (%s) does not contain packages matching %s", Target.Path, e.Pattern)
		}
		return fmt.Sprintf("main module (%s) does not contain package %s", Target.Path, e.Pattern)
	}

	found := ""
	if r := e.Replacement; r.Path != "" {
		replacement := r.Path
		if r.Version != "" {
			replacement = fmt.Sprintf("%s@%s", r.Path, r.Version)
		}
		if e.Query == e.Mod.Version {
			found = fmt.Sprintf(" (replaced by %s)", replacement)
		} else {
			found = fmt.Sprintf(" (%s, replaced by %s)", e.Mod.Version, replacement)
		}
	} else if e.Query != e.Mod.Version {
		found = fmt.Sprintf(" (%s)", e.Mod.Version)
	}

	if strings.Contains(e.Pattern, "...") {
		return fmt.Sprintf("module %s@%s found%s, but does not contain packages matching %s", e.Mod.Path, e.Query, found, e.Pattern)
	}
	return fmt.Sprintf("module %s@%s found%s, but does not contain package %s", e.Mod.Path, e.Query, found, e.Pattern)
}

func (e *PackageNotInModuleError) ImportPath() string {
	if !strings.Contains(e.Pattern, "...") {
		return e.Pattern
	}
	return ""
}

// moduleHasRootPackage返回模块m是否包含包m.Path。
func moduleHasRootPackage(ctx context.Context, m module.Version) (bool, error) {
	needSum := false
	root, isLocal, err := fetch(ctx, m, needSum)
	if err != nil {
		return false, err
	}
	_, ok, err := dirInModule(m.Path, m.Path, root, isLocal)
	return ok, err
}

// versionHasGoMod返回一个版本是否有go.mod文件。
// None
// versionHasGoMod获取go.mod文件（可能是假的），如果是，则获取true
// 包含除具有相同路径的模块指令以外的任何内容。当
// 模块没有真正的go.mod文件，go命令的作用就好像它有一个一样
// 它只包含一个模块指令。之后创建的普通go.mod文件
// 1.12至少有一个go指令。
// None
// 这个函数是一个启发式函数，因为可以提交一个
// 通过这个测试。然而，我们只需要一个试探法来确定
// +不兼容的版本可能是“最新版本”，这就是此功能所使用的版本
// 对于
// None
// 这种启发式方法之所以有用，有两个原因：第一，当使用代理时，
// 这使我们能够从.mod端点获取数据，这比.zip要快得多
// 终点。即使.zip文件包含
// 具有不同内容的go.mod。第二，如果我们不取.zip，那么
// 我们不需要在go.sum中验证它。这使得“go list-m-u”更快
// 而且更简单。
func versionHasGoMod(_ context.Context, m module.Version) (bool, error) {
	_, data, err := rawGoModData(m)
	if err != nil {
		return false, err
	}
	isFake := bytes.Equal(data, modfetch.LegacyGoMod(m.Path))
	return !isFake, nil
}

// versionRepo是modfetch.Repo的子集，它可以报告有关的信息
// 可用版本，但无法获取特定的源文件。
type versionRepo interface {
	ModulePath() string
	Versions(prefix string) ([]string, error)
	Stat(rev string) (*modfetch.RevInfo, error)
	Latest() (*modfetch.RevInfo, error)
}

var _ versionRepo = modfetch.Repo(nil)

func lookupRepo(proxy, path string) (repo versionRepo, err error) {
	err = module.CheckPath(path)
	if err == nil {
		repo = modfetch.Lookup(proxy, path)
	} else {
		repo = emptyRepo{path: path, err: err}
	}

	if index == nil {
		return repo, err
	}
	if _, ok := index.highestReplaced[path]; !ok {
		return repo, err
	}

	return &replacementRepo{repo: repo}, nil
}

// EmptyReporto是不包含任何版本的versionRepo。
type emptyRepo struct {
	path string
	err  error
}

var _ versionRepo = emptyRepo{}

func (er emptyRepo) ModulePath() string                         { return er.path }
func (er emptyRepo) Versions(prefix string) ([]string, error)   { return nil, nil }
func (er emptyRepo) Stat(rev string) (*modfetch.RevInfo, error) { return nil, er.err }
func (er emptyRepo) Latest() (*modfetch.RevInfo, error)         { return nil, er.err }

// replacementRepo扩展versionRepo以包含替换版本
// （如果有）在主模块的go.mod文件中找到。
// None
// replacementRepo会抑制不存在的“未找到”错误
// 模块，因此只应为
// 实际上有一个或多个有效替换。
type replacementRepo struct {
	repo versionRepo
}

var _ versionRepo = (*replacementRepo)(nil)

func (rr *replacementRepo) ModulePath() string { return rr.repo.ModulePath() }

// Versions返回rr.repo中包含任何匹配项的版本
// 替换版本。
func (rr *replacementRepo) Versions(prefix string) ([]string, error) {
	repoVersions, err := rr.repo.Versions(prefix)
	if err != nil && !errors.Is(err, os.ErrNotExist) {
		return nil, err
	}

	versions := repoVersions
	if index != nil && len(index.replace) > 0 {
		path := rr.ModulePath()
		for m, _ := range index.replace {
			if m.Path == path && strings.HasPrefix(m.Version, prefix) && m.Version != "" && !module.IsPseudoVersion(m.Version) {
				versions = append(versions, m.Version)
			}
		}
	}

	if len(versions) == len(repoVersions) { // 未添加替换版本。
		return versions, nil
	}

	sort.Slice(versions, func(i, j int) bool {
		return semver.Compare(versions[i], versions[j]) < 0
	})
	str.Uniq(&versions)
	return versions, nil
}

func (rr *replacementRepo) Stat(rev string) (*modfetch.RevInfo, error) {
	info, err := rr.repo.Stat(rev)
	if err == nil || index == nil || len(index.replace) == 0 {
		return info, err
	}

	v := module.CanonicalVersion(rev)
	if v != rev {
		// go.mod文件中的替换仅列出规范语义版本，
		// 因此，非规范版本不可能有替代版本。
		return info, err
	}

	path := rr.ModulePath()
	_, pathMajor, ok := module.SplitPathVersion(path)
	if ok && pathMajor == "" {
		if err := module.CheckPathMajor(v, pathMajor); err != nil && semver.Build(v) == "" {
			v += "+incompatible"
		}
	}

	if r := Replacement(module.Version{Path: path, Version: v}); r.Path == "" {
		return info, err
	}
	return rr.replacementStat(v)
}

func (rr *replacementRepo) Latest() (*modfetch.RevInfo, error) {
	info, err := rr.repo.Latest()

	if index != nil {
		path := rr.ModulePath()
		if v, ok := index.highestReplaced[path]; ok {
			if v == "" {
				// 唯一的替换是一个未指定版本的通配符，因此
				// 合成一个伪版本，其中包含适当的主版本和
				// 低于任何实际时间戳的时间戳。这样，如果主模块是
				// 从其他模块中使用，用户将能够升级
				// 对他们选择的任何真实版本的要求。
				if _, pathMajor, ok := module.SplitPathVersion(path); ok && len(pathMajor) > 0 {
					v = module.PseudoVersion(pathMajor[1:], "", time.Time{}, "000000000000")
				} else {
					v = module.PseudoVersion("v0", "", time.Time{}, "000000000000")
				}
			}

			if err != nil || semver.Compare(v, info.Version) > 0 {
				return rr.replacementStat(v)
			}
		}
	}

	return info, err
}

func (rr *replacementRepo) replacementStat(v string) (*modfetch.RevInfo, error) {
	rev := &modfetch.RevInfo{Version: v}
	if module.IsPseudoVersion(v) {
		rev.Time, _ = module.PseudoVersionTime(v)
		rev.Short, _ = module.PseudoVersionRev(v)
	}
	return rev, nil
}

// QueryMatchesMainModuleError表示查询请求
// 无法满足的主模块版本。
// （无法更改主模块的版本。）
type QueryMatchesMainModuleError struct {
	Pattern string
	Query   string
}

func (e *QueryMatchesMainModuleError) Error() string {
	if e.Pattern == Target.Path {
		return fmt.Sprintf("can't request version %q of the main module (%s)", e.Query, e.Pattern)
	}

	return fmt.Sprintf("can't request version %q of pattern %q that includes the main module (%s)", e.Query, e.Pattern, Target.Path)
}

// QueryMatchesPackagesInMainModuleError表示无法删除查询
// 满意，因为它与主模块中的一个或多个包相匹配。
type QueryMatchesPackagesInMainModuleError struct {
	Pattern  string
	Query    string
	Packages []string
}

func (e *QueryMatchesPackagesInMainModuleError) Error() string {
	if len(e.Packages) > 1 {
		return fmt.Sprintf("pattern %s matches %d packages in the main module, so can't request version %s", e.Pattern, len(e.Packages), e.Query)
	}

	if search.IsMetaPackage(e.Pattern) || strings.Contains(e.Pattern, "...") {
		return fmt.Sprintf("pattern %s matches package %s in the main module, so can't request version %s", e.Pattern, e.Packages[0], e.Query)
	}

	return fmt.Sprintf("package %s is in the main module, so can't request version %s", e.Packages[0], e.Query)
}
